{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "张量的数学运算.ipynb",
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fLZ40zasvuCf"
      },
      "source": [
        "# 张量的数学运算\n",
        "\n",
        "张量数学运算主要有：标量运算，向量运算，矩阵运算。另外我们会介绍张量运算的广播机制。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E7F7JfJ0p1qs"
      },
      "source": [
        "# 标量运算\n",
        "\n",
        "张量的数学运算符可以分为标量运算符、向量运算符、以及矩阵运算符。加减乘除乘方，以及三角函数，指数，对数等常见函数，逻辑比较运算符等都是标量运算符。\n",
        "\n",
        "标量运算符的特点是对张量实施逐元素运算。有些标量运算符对常用的数学运算符进行了重载。并且支持类似 numpy 的广播特性。许多标量运算符都在 tf.math 模块下。\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "BVDN_ZRsqZBf"
      },
      "source": [
        "import tensorflow as tf \n",
        "import numpy as np "
      ],
      "execution_count": 1,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "1Sbi0E7aqdPg",
        "outputId": "4c8b8d7c-64ca-4471-8d64-a32c456119b7",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 289
        }
      },
      "source": [
        "a = tf.constant([[1.0,2],[-3,4.0]])\n",
        "b = tf.constant([[5.0,6],[7.0,8.0]])\n",
        "\n",
        "tf.print(a + b)  #运算符重载\n",
        "tf.print(a - b)\n",
        "tf.print(a * b)\n",
        "tf.print(a / b)\n",
        "tf.print(a ** 2)\n",
        "tf.print(a ** 0.5)\n",
        "tf.print(a % 3)\n",
        "tf.print(a >= 2)"
      ],
      "execution_count": 19,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[6 8]\n",
            " [4 12]]\n",
            "[[-4 -4]\n",
            " [-10 -4]]\n",
            "[[5 12]\n",
            " [-21 32]]\n",
            "[[0.2 0.333333343]\n",
            " [-0.428571433 0.5]]\n",
            "[[1 4]\n",
            " [9 16]]\n",
            "[[1 1.41421354]\n",
            " [-nan 2]]\n",
            "[[1 2]\n",
            " [-0 1]]\n",
            "[[0 1]\n",
            " [0 1]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "XkcmSvwGrDjO",
        "outputId": "24dd1868-10d2-46a1-c8c4-10f311e05ee4",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 85
        }
      },
      "source": [
        "tf.print((a>=2)&(a<=3))\n",
        "\n",
        "tf.print((a>=2)|(a<=3))\n",
        "\n",
        "tf.print(a==5)\n",
        "\n",
        "tf.print(tf.sqrt(a))"
      ],
      "execution_count": 15,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[0 0]\n",
            "[1 1]\n",
            "[0 0]\n",
            "[1 2.82842708]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "c0R9mC8OrPU-",
        "outputId": "a3ce34c3-0230-450d-94e7-f4815b9bd503",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "a = tf.constant([1.0,8.0])\n",
        "b = tf.constant([5.0,6.0])\n",
        "c = tf.constant([6.0,7.0])\n",
        "tf.add_n([a,b,c])"
      ],
      "execution_count": 14,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<tf.Tensor: shape=(2,), dtype=float32, numpy=array([12., 21.], dtype=float32)>"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 14
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "EPPsaWQrrVIh"
      },
      "source": [
        "tf.print(tf.maximum(a,b))\n",
        "\n",
        "tf.print(tf.minimum(a,b))\n",
        "\n",
        "x = tf.constant([2.6,-2.7])\n",
        "\n",
        "tf.print(tf.math.round(x)) #保留整数部分，四舍五入\n",
        "tf.print(tf.math.floor(x)) #保留整数部分，向下归整\n",
        "tf.print(tf.math.ceil(x))  #保留整数部分，向上归整\n",
        "\n",
        "# 幅值裁剪\n",
        "x = tf.constant([0.9,-0.8,100.0,-20.0,0.7])\n",
        "y = tf.clip_by_value(x,clip_value_min=-1,clip_value_max=1)\n",
        "z = tf.clip_by_norm(x,clip_norm = 3)\n",
        "tf.print(y)\n",
        "tf.print(z)"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5_IF8J9CvGQI"
      },
      "source": [
        "# 向量运算\n",
        "\n",
        "向量运算符只在一个特定轴上运算，将一个向量映射到一个标量或者另外一个向量。 许多向量运算符都以 reduce 开头。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ydfU5bj4rwON",
        "outputId": "c831b778-0d68-4ce0-bd4e-528018b00c8f",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 323
        }
      },
      "source": [
        "# 向量 reduce\n",
        "a = tf.range(1,10)\n",
        "tf.print(tf.reduce_sum(a))\n",
        "tf.print(tf.reduce_mean(a))\n",
        "tf.print(tf.reduce_max(a))\n",
        "tf.print(tf.reduce_min(a))\n",
        "tf.print(tf.reduce_prod(a))\n",
        "\n",
        "# 张量指定维度进行reduce\n",
        "b = tf.reshape(a,(3,3))\n",
        "tf.print(tf.reduce_sum(b, axis=1, keepdims=True))\n",
        "tf.print(tf.reduce_sum(b, axis=0, keepdims=True))\n",
        "\n",
        "# bool类型的reduce\n",
        "p = tf.constant([True,False,False])\n",
        "q = tf.constant([False,False,True])\n",
        "tf.print(tf.reduce_all(p))\n",
        "tf.print(tf.reduce_any(q))\n",
        "\n",
        "# 利用tf.foldr实现tf.reduce_sum\n",
        "s = tf.foldr(lambda a,b:a+b,tf.range(10)) \n",
        "tf.print(s)\n",
        "\n",
        "# cum扫描累积\n",
        "a = tf.range(1,10)\n",
        "tf.print(tf.math.cumsum(a))\n",
        "tf.print(tf.math.cumprod(a))\n",
        "\n",
        "# arg最大最小值索引\n",
        "a = tf.range(1,10)\n",
        "tf.print(tf.argmax(a))\n",
        "tf.print(tf.argmin(a))\n",
        "\n",
        "# tf.math.top_k可以用于对张量排序\n",
        "a = tf.constant([1,3,7,5,4,8])\n",
        "\n",
        "values,indices = tf.math.top_k(a,3,sorted=True)\n",
        "tf.print(values)\n",
        "tf.print(indices)\n",
        "\n",
        "#利用tf.math.top_k可以在TensorFlow中实现KNN算法"
      ],
      "execution_count": 22,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "45\n",
            "5\n",
            "9\n",
            "1\n",
            "362880\n",
            "[[6]\n",
            " [15]\n",
            " [24]]\n",
            "[[12 15 18]]\n",
            "0\n",
            "1\n",
            "45\n",
            "[1 3 6 ... 28 36 45]\n",
            "[1 2 6 ... 5040 40320 362880]\n",
            "8\n",
            "0\n",
            "[8 7 5]\n",
            "[5 2 3]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "D8QINqstviL0"
      },
      "source": [
        "# 矩阵运算\n",
        "\n",
        "\n",
        "矩阵必须是二维的。类似tf.constant([1,2,3])这样的不是矩阵。矩阵运算包括：矩阵乘法，矩阵转置，矩阵逆，矩阵求迹，矩阵范数，矩阵行列式，矩阵求特征值，矩阵分解等运算。除了一些常用的运算外，大部分和矩阵有关的运算都在tf.linalg子包中。\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "i5Qg1OXhvoVl",
        "outputId": "be2ba0d8-e8fb-491c-eb3f-33b28bc5a0a3",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 357
        }
      },
      "source": [
        "# 矩阵乘法\n",
        "a = tf.constant([[1,2],[3,4]])\n",
        "b = tf.constant([[2,0],[0,2]])\n",
        "tf.print(a@b)  #等价于tf.matmul(a,b)\n",
        "\n",
        "# 矩阵转置\n",
        "a = tf.constant([[1,2],[3,4]])\n",
        "tf.transpose(a)\n",
        "\n",
        "# 矩阵逆，必须为tf.float32或tf.double类型\n",
        "a = tf.constant([[1.0,2],[3,4]],dtype = tf.float32)\n",
        "tf.linalg.inv(a)\n",
        "\n",
        "# 矩阵求 trace\n",
        "a = tf.constant([[1.0,2],[3,4]],dtype = tf.float32)\n",
        "tf.linalg.trace(a)\n",
        "\n",
        "# 矩阵求范数\n",
        "a = tf.constant([[1.0,2],[3,4]])\n",
        "tf.linalg.norm(a)\n",
        "\n",
        "# 矩阵行列式\n",
        "a = tf.constant([[1.0,2],[3,4]])\n",
        "tf.linalg.det(a)\n",
        "\n",
        "# 矩阵特征值\n",
        "a = tf.constant([[1.0,2],[-5,4]])\n",
        "tf.linalg.eigvals(a)\n",
        "\n",
        "# 矩阵QR分解, 将一个方阵分解为一个正交矩阵q和上三角矩阵r\n",
        "# QR分解实际上是对矩阵a实施Schmidt正交化得到q\n",
        "\n",
        "a = tf.constant([[1.0,2.0],[3.0,4.0]],dtype = tf.float32)\n",
        "q,r = tf.linalg.qr(a)\n",
        "tf.print(q)\n",
        "tf.print(r)\n",
        "tf.print(q@r)\n",
        "\n",
        "# 矩阵svd分解\n",
        "# svd分解可以将任意一个矩阵分解为一个正交矩阵u,一个对角阵s和一个正交矩阵v.t()的乘积\n",
        "# svd常用于矩阵压缩和降维\n",
        "\n",
        "a  = tf.constant([[1.0,2.0],[3.0,4.0],[5.0,6.0]], dtype = tf.float32)\n",
        "s,u,v = tf.linalg.svd(a)\n",
        "tf.print(u,\"\\n\")\n",
        "tf.print(s,\"\\n\")\n",
        "tf.print(v,\"\\n\")\n",
        "tf.print(u@tf.linalg.diag(s)@tf.transpose(v))\n",
        "\n",
        "# 利用svd分解可以在TensorFlow中实现主成分分析降维"
      ],
      "execution_count": 25,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "[[2 4]\n",
            " [6 8]]\n",
            "[[-0.316227794 -0.948683321]\n",
            " [-0.948683321 0.316227764]]\n",
            "[[-3.1622777 -4.4271884]\n",
            " [0 -0.632455409]]\n",
            "[[1.00000012 1.99999988]\n",
            " [3 3.99999976]]\n",
            "[[0.229847804 -0.883461118]\n",
            " [0.524744928 -0.240782872]\n",
            " [0.819642 0.401896209]] \n",
            "\n",
            "[9.52552 0.514300048] \n",
            "\n",
            "[[0.619629562 0.784894466]\n",
            " [0.784894466 -0.619629562]] \n",
            "\n",
            "[[1.00000155 2.00000095]\n",
            " [3.00000167 4.00000191]\n",
            " [5.00000191 6.00000191]]\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-0577O52wPyG"
      },
      "source": [
        "# 广播机制\n",
        "\n",
        "TensorFlow的广播规则和numpy是一样的:\n",
        "\n",
        "- 1、如果张量的维度不同，将维度较小的张量进行扩展，直到两个张量的维度都一样。\n",
        "- 2、如果两个张量在某个维度上的长度是相同的，或者其中一个张量在该维度上的长度为1，那么我们就说这两个张量在该维度上是相容的。\n",
        "- 3、如果两个张量在所有维度上都是相容的，它们就能使用广播。\n",
        "- 4、广播之后，每个维度的长度将取两个张量在该维度长度的较大值。\n",
        "- 5、在任何一个维度上，如果一个张量的长度为1，另一个张量长度大于1，那么在该维度上，就好像是对第一个张量进行了复制。\n",
        "\n",
        "tf.broadcast_to 以显式的方式按照广播机制扩展张量的维度。"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "DxkUGtWlwVEV",
        "outputId": "33b2ed37-da00-42e4-9b12-edda123dc334",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 85
        }
      },
      "source": [
        "a = tf.constant([1,2,3])\n",
        "b = tf.constant([[0,0,0],[1,1,1],[2,2,2]])\n",
        "b + a  # 等价于 b + tf.broadcast_to(a,b.shape)\n",
        "\n",
        "tf.broadcast_to(a,b.shape)\n",
        "\n",
        "# 计算广播后计算结果的形状，静态形状，TensorShape类型参数\n",
        "tf.broadcast_static_shape(a.shape,b.shape)\n",
        "\n",
        "# 计算广播后计算结果的形状，动态形状，Tensor类型参数\n",
        "c = tf.constant([1,2,3])\n",
        "d = tf.constant([[1],[2],[3]])\n",
        "tf.broadcast_dynamic_shape(tf.shape(c),tf.shape(d))\n",
        "\n",
        "# 广播效果\n",
        "c+d # 等价于 tf.broadcast_to(c,[3,3]) + tf.broadcast_to(d,[3,3])"
      ],
      "execution_count": 26,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<tf.Tensor: shape=(3, 3), dtype=int32, numpy=\n",
              "array([[2, 3, 4],\n",
              "       [3, 4, 5],\n",
              "       [4, 5, 6]], dtype=int32)>"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 26
        }
      ]
    }
  ]
}